package com.duojuhe.security.shiro;


import com.duojuhe.common.bean.SystemMenuInfoVo;
import com.duojuhe.common.bean.UserTokenInfoVo;
import com.duojuhe.cache.LoginUserTokenCache;
import com.duojuhe.common.config.DuoJuHeConfig;
import com.duojuhe.common.constant.SystemConstants;
import com.duojuhe.common.enums.SystemEnum;
import com.duojuhe.common.utils.iputil.IPUtils;
import com.duojuhe.common.utils.token.TokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Objects;


/**
 * shiro 核心验证
 *
 * @date 2018/2/9.
 **/
@Slf4j
public class ShiroRealm extends AuthorizingRealm {
    @Resource
    private DuoJuHeConfig duoJuHeConfig;
    @Resource
    private LoginUserTokenCache loginUserTokenCache;

    /**
     * 该Realm仅支持自定义的StatelessAuthenticationToken类型Token，其他类型处理将会抛出异常
     *
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof StatelessAuthenticationToken;
    }


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) throws AuthenticationException {
        try {
            if (principalCollection == null || principalCollection.fromRealm(getName()) == null) {
                return null;
            }
            UserTokenInfoVo userTokenInfoVo = (UserTokenInfoVo) principalCollection.getPrimaryPrincipal();
            if (userTokenInfoVo == null) {
                return null;
            }
            List<SystemMenuInfoVo> menuList = userTokenInfoVo.getMenuInfoVOList();
            if (menuList == null || menuList.isEmpty()) {
                return null;
            }
            // 权限信息对象info,用来存放查出的用户的所有的角色（role）及权限（permission）
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            for (SystemMenuInfoVo menu : menuList) {
                info.addStringPermission(menu.getMenuCode());
            }
            return info;
        } catch (Exception e) {
            return null;
        }
    }


    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws ShiroException {
        StatelessAuthenticationToken statelessAuthenticationToken = (StatelessAuthenticationToken) auth;
        Map map = (Map) statelessAuthenticationToken.getPrincipal();
        String token = (String) map.get(TokenUtils.TOKEN_HEADER);
        String loginResource = (String) map.get(StatelessAuthenticationToken.LOGIN_RESOURCE);
        if (SystemEnum.LOGIN_RESOURCE.VISITORS_USER.getKey().equals(loginResource)) {
            UserTokenInfoVo userTokenInfoVo = loginUserTokenCache.getUserTokenInfoVoCacheByToken(token);
            if (userTokenInfoVo == null) {
                HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
                String requestIp = IPUtils.getClientIPByIpHeader(request, duoJuHeConfig.getGetAdminIpHeader());
                String unknownId = SystemConstants.UNKNOWN_ID;
                userTokenInfoVo = new UserTokenInfoVo();
                userTokenInfoVo.setUserId(requestIp);
                userTokenInfoVo.setUserNumber(unknownId);
                userTokenInfoVo.setPostId(unknownId);
                userTokenInfoVo.setCreateDeptId(unknownId);
                userTokenInfoVo.setTenantId(unknownId);
                userTokenInfoVo.setTenantNumber(unknownId);
                userTokenInfoVo.setLoginIp(requestIp);
                userTokenInfoVo.setUserLoginSource(SystemEnum.LOGIN_RESOURCE.VISITORS_USER.getKey());
                return new SimpleAuthenticationInfo(userTokenInfoVo, userTokenInfoVo.getUserId(), this.getName());
            } else {
                return new SimpleAuthenticationInfo(userTokenInfoVo, token, this.getName());
            }
        } else {
            UserTokenInfoVo userTokenInfoVo = loginUserTokenCache.getUserTokenInfoVoCacheByToken(token);
            // 解密获得登录名，用于和数据库进行对比
            if (userTokenInfoVo == null) {
                throw new AuthenticationException();
            }
            if (loginResource != null && !loginResource.equals(userTokenInfoVo.getUserLoginSource())) {
                throw new AuthenticationException();
            }
            if (userTokenInfoVo.getKickOut()) {
                //被强制踢出
                throw new CredentialsException();
            }
            return new SimpleAuthenticationInfo(userTokenInfoVo, token, this.getName());
        }
    }
}